

#include "thread_pool.h"
#include <functional>
#include <memory>
#include <mutex>
#include <strings.h>
#include <thread>
#include <unistd.h>
#include <utility>
#include <vector>


ThreadQueue::ThreadQueue()
{

}

ThreadQueue::~ThreadQueue()
{
	if (m_thread.joinable())
	{
		m_thread.join();
	}
}

ThreadPoolManager::ThreadPoolManager(int size)
{
	m_size = size;
	m_push_index = 0;
}

void ThreadPoolManager::Run()
{
	m_is_running = true;
	for (int index = 0; index < m_size; index++)
	{
		auto thd_queue = std::make_shared<ThreadQueue>();

		m_thread_queue_vec.push_back(thd_queue);

		thd_queue->m_thread_index = index;
		thd_queue->m_thread       = std::thread([this, index](){

			RunThreadQueue(index);
		});
	}
}

void ThreadPoolManager::PushTask(std::function<void ()> &&func_task)
{

	auto thd = m_thread_queue_vec[(m_push_index++)%m_thread_queue_vec.size()];

	std::unique_lock<std::mutex> lk(thd->m_mutex);
	thd->m_task_vec.push_back(std::forward<std::function<void()>>(func_task));
	lk.unlock();
	thd->m_cond.notify_all();
}

void ThreadPoolManager::PushTaskByIndex(int index, std::function<void ()> &&func_task)
{
	auto thd = m_thread_queue_vec[(index % m_thread_queue_vec.size())];

	std::unique_lock<std::mutex> lk(thd->m_mutex);
	thd->m_task_vec.push_back(std::forward<std::function<void()>>(func_task));
	lk.unlock();
	thd->m_cond.notify_all();
}

void ThreadPoolManager::RunThreadQueue(int index)
{
	auto thd = m_thread_queue_vec[index];
	std::vector<std::function<void()>> tasks;
	
	while (m_is_running)
	{
		{
			std::unique_lock<std::mutex> lk(thd->m_mutex);
			thd->m_cond.wait(lk, [&thd](){

				if (thd->m_task_vec.empty())
				{
					return false;
				}
				return true;
			});
			thd->m_task_vec.swap(tasks);
		}
		for (auto & data : tasks)
		{
			data();
		}
		tasks.clear();
		tasks.shrink_to_fit();
	}
}